package com.ht.impl;

import com.htgraph.util.GraphLoader;
import com.mxgraph.io.mxCodec;
import com.mxgraph.model.mxCell;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.util.mxGraphActions;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxUtils;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxGraphView;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

/**
 * Created by IntelliJ IDEA.
 *
 * @author Dong Shufeng
 *         Date: 2010-7-2
 */
public class CustomGraphCompent extends mxGraphComponent {
    private boolean movable = true;
    public static final int MOUSEDRAG_ZOOM = 0;
    public static final int MOUSEDRAG_DRAG = 1;
    private int dragModel = 1;
    Component glass = null;
    boolean isGlassVisible = false;

    public CustomGraphCompent(mxGraph graph) {
        super(graph);
    }

    public CustomGraphCompent(boolean scaleAble) {
        this(new CustomGraph(), scaleAble);
    }

    public CustomGraphCompent(final mxGraph graph, boolean scaleAble) {
        super(graph);
        this.graph = graph;
        mxCodec codec = new mxCodec();
        Document doc = mxUtils.loadDocument(getClass().getResource("/config/ht-style.xml").toString());
        codec.decode(doc.getDocumentElement(), graph.getStylesheet());
        //graph.getStylesheet().getDefaultEdgeStyle().put("edgeStyle", "htEdgeStyle");
        installListeners();
        if (scaleAble)
            addMouseWheelListener(new MouseWheelListener() {
                public void mouseWheelMoved(MouseWheelEvent e) {
                    if (e.getWheelRotation() < 0) {
                        mxGraphActions.getZoomInAction().actionPerformed(new ActionEvent(e.getSource(), e.getID(), "ZoomIn"));
                    } else {
                        mxGraphActions.getZoomOutAction().actionPerformed(new ActionEvent(e.getSource(), e.getID(), "ZoomOut"));
                    }
                }
            });
    }

    private void initGlassComponent() {
        Container parentComponent = getParent();
        int depth = 0;
        while (true) {
            if(parentComponent == null)
                break;
            if (depth > 20)
                break;
            if (parentComponent instanceof JInternalFrame) {
                glass = ((JInternalFrame) parentComponent).getGlassPane();
                break;
            } else
                parentComponent = parentComponent.getParent();
            depth++;
        }
        if (glass == null)
            glass = this.getGraphControl();
    }

    public void installListeners() {
        MouseAdapter moveWindowListener = new MouseAdapter() {
            private Point first = null;
            private Point firstP = null;
            private Point endP = null;
            @Override
            public void mouseDragged(MouseEvent e) {
                if(!movable)
                    return;
                Point pt = e.getLocationOnScreen();
                if (isGlassVisible())
                    glass.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
                else
                    e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
                if(dragModel == MOUSEDRAG_ZOOM)
                    return;
                e.consume();
                double scale = graph.getView().getScale();
                int dx = (int) (pt.getX() - first.getX());
                int dy = (int) (pt.getY() - first.getY());
                first = pt;
                double x = graph.getView().getTranslate().getX() + dx / scale;
                double y = graph.getView().getTranslate().getY() + dy / scale;
                graph.getView().setTranslate(new mxPoint(x, y));
            }

            @Override
            public void mousePressed(MouseEvent e) {
                if (glass == null)
                    initGlassComponent();
                Object obj = graph.getSelectionCell();
                first = e.getLocationOnScreen();
                firstP = e.getLocationOnScreen();
                if (obj == null) {
                    if (isGlassVisible())
                        glass.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
                    else
                        e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
                }
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                if (isGlassVisible())
                    glass.setCursor(Cursor.getDefaultCursor());
                else
                    e.getComponent().setCursor(Cursor.getDefaultCursor());
                endP = e.getLocationOnScreen();
                if(dragModel == MOUSEDRAG_DRAG)
                    return;
                if(Math.abs(firstP.getX() - endP.getX()) < 5.0 || Math.abs(firstP.getY() - endP.getY()) < 5.0)
                    return;
                double oldScale = graph.getView().getScale();
                double actualGraphWidth = graph.getView().getGraphBounds().getWidth() / oldScale;
                double actualGraphHeight = graph.getView().getGraphBounds().getHeight() / oldScale;
                double width = getWidth();
                double height = getHeight();
                double scale1 = width * 0.87 / actualGraphWidth;
                double scale2 = height * 0.87 / actualGraphHeight;
                double scale = scale1 < scale2 ? scale1 : scale2;
                if (scale < 1e-6) scale = oldScale;
                double offsetX = (width - actualGraphWidth * scale);
                double offsetY = (height - actualGraphHeight * scale);
                if (offsetX < 0)
                    offsetX = 20.0;
                if (offsetY < 0)
                    offsetY = 20.0;
                double xfac = getWidth() / Math.abs(endP.getX() - firstP.getX());
                double yfac = getHeight() / Math.abs(endP.getY() - firstP.getY());
                double fac = xfac > yfac ? xfac : yfac;
                fac = oldScale * fac;
                if (fac < 1e-6) fac = oldScale;
                Rectangle rec = new Rectangle(firstP);
                rec.add(endP);
                double dx = rec.getX() / oldScale;
                double dy = rec.getY() / oldScale;
                double x = graph.getView().getTranslate().getX() - dx;
                double y = graph.getView().getTranslate().getY() - dy + offsetY / oldScale;
                graph.getView().scaleAndTranslate(fac, x, y);
                refresh();
            }

            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
                Object obj = graph.getSelectionCell();
                if (obj == null)
                    return;
                mxCell cell = (mxCell) obj;
                if (SwingUtilities.isRightMouseButton(e)) {
                    rightMouseClickAction(e, cell);
                } else if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 1) {
                    leftMouseClickAction(e, cell);
                } else if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) {
                    doubleMouseClickAction(e, cell);
                }
            }

            @Override
            public void mouseMoved(MouseEvent e) {
//                super.mouseMoved(e);
                e.getComponent().setCursor(Cursor.getDefaultCursor());
            }

            @Override
            public void mouseEntered(MouseEvent e) {
//                super.mouseEntered(e);
                e.getComponent().setCursor(Cursor.getDefaultCursor());
            }

            @Override
            public void mouseExited(MouseEvent e) {
//                super.mouseExited(e);
                e.getComponent().setCursor(Cursor.getDefaultCursor());
            }
        };
        getGraphControl().addMouseListener(moveWindowListener);
        getGraphControl().addMouseMotionListener(moveWindowListener);
    }

    public void zoom(double factor) {
        //mxGraphView view = graph.getView();
        //double oldScale = view.getScale();
        //double newScale = (double) ((int) (oldScale * 100 * factor)) / 100;
        //if (newScale < 0.05) return;
        //
        //if (newScale != view.getScale() && newScale > 0.01) {
        //    mxPoint translate = (pageVisible && centerPage) ? getPageTranslate(newScale)
        //            : getCenterTranslate(oldScale, newScale);//this is only line that diffirent from father class' method
        //    graph.getView().scaleAndTranslate(newScale, translate.getX(), translate.getY());
        //
        //    if (keepSelectionVisibleOnZoom && !graph.isSelectionEmpty()) {
        //        getGraphControl().scrollRectToVisible(
        //                view.getBoundingBox(graph.getSelectionCells()).getRectangle());
        //    } else {
        //        maintainScrollBar(true, factor, centerZoom);
        //        maintainScrollBar(false, factor, centerZoom);
        //    }
        //}
         mxGraphView view = graph.getView();
        double newScale = (double) ((int) (view.getScale() * 100 * factor)) / 100;

        if (newScale != view.getScale() && newScale > 0.01) {
            mxPoint translate =view.getTranslate();
            graph.getView().scaleAndTranslate(newScale, translate.getX(),
                    translate.getY());

            if (keepSelectionVisibleOnZoom && !graph.isSelectionEmpty()) {
                getGraphControl().scrollRectToVisible(
                        view.getBoundingBox(graph.getSelectionCells())
                                .getRectangle());
            } else {
                maintainScrollBar(true, factor, centerZoom);
                maintainScrollBar(false, factor, centerZoom);
            }
        }
    }

    protected mxPoint getCenterTranslate(double oldScale, double newScale) {
        double actualGraphWidth = graph.getView().getGraphBounds().getWidth() / oldScale;
        double actualGraphHeight = graph.getView().getGraphBounds().getHeight() / oldScale;
        double dx = getGraph().getView().getTranslate().getX() / oldScale - actualGraphWidth * (newScale - oldScale) / 2.0;
        double dy = getGraph().getView().getTranslate().getY() / oldScale - actualGraphHeight * (newScale - oldScale) / 2.0;
        return new mxPoint(dx * newScale, dy * newScale);
    }

    public boolean isGlassVisible() {
        return isGlassVisible;
    }

    public void setGlassVisible(boolean glassVisible) {
        isGlassVisible = glassVisible;
    }

    protected void leftMouseClickAction(MouseEvent e, mxCell cell) {
    }

    protected void rightMouseClickAction(MouseEvent e, final mxCell cell) {
    }

    protected void doubleMouseClickAction(MouseEvent e, mxCell cell) {
    }


    /**
     * A graph that creates new edges from a given template edge.
     */

    public static class CustomGraph extends mxGraph {

        public String getToolTipForCell(Object object) {
            mxCell cell = null;
            if (object instanceof mxCell)
                cell = (mxCell) object;
            if (cell == null)
                return super.convertValueToString(object);
            Element ele;
            if (cell.getValue() != null && cell.getValue() instanceof Element) {
                ele = (Element) cell.getValue();

                String desc = ele.getAttribute(ResourceTypeCons.RESOURCE_DESCRIBE);
                return "<html>" + desc + (desc.trim().equals("")? "" : "<br>") + ele.getAttribute(ResourceTypeCons.RESOURCE_ID) + ele.getAttribute(ResourceTypeCons.EXPRESSION_DEFINE) + "</html>";
            }
            return "";
        }

        public String convertValueToString(Object object) {
            mxCell cell = null;
            if (object instanceof mxCell)
                cell = (mxCell) object;
            if (cell == null)
                return super.convertValueToString(object);
            Element ele;
            if (cell.getValue() != null && cell.getValue() instanceof Element) {
                ele = (Element) cell.getValue();
                if (!ele.getAttribute("deviceName").equals(""))
                    return ele.getAttribute("deviceName");
                return ele.getTextContent();
            }
            return super.convertValueToString(object);
        }

        public boolean isCellResizable(Object o) {
            return false;
        }
    }


    public void setMxeSource(String file) {
        try {
            setMxeSource(new FileInputStream(file));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public void setMxeSource(InputStream stream) {
        long start = System.currentTimeMillis();
        Document document = GraphLoader.parse(stream);
        System.out.println("Time used for parsing xml:" + (System.currentTimeMillis() - start) + "ms");
        mxCodec codec = new mxCodec(document);
        //((mxGraphModel) graph.getModel()).clear();
        start = System.currentTimeMillis();
        codec.decode(document.getDocumentElement(), graph.getModel());
        System.out.println("Time used for decode xml:" + (System.currentTimeMillis() - start) + "ms");
        setToolTips(true);
    }


    public int getDragModel() {
        return dragModel;
    }

    public void setDragModel(int dragModel) {
        this.dragModel = dragModel;
    }


    public boolean isMovable() {
        return movable;
    }

    public void setMovable(boolean movable) {
        this.movable = movable;
    }


    public static void main(String[] args) {
        mxConstants.SHADOW_COLOR = Color.LIGHT_GRAY;
        mxConstants.STYLE_SHADOW = "true";
        CustomGraphCompent component = new CustomGraphCompent(true);

        CustomGraphViewer editor = new CustomGraphViewer("", component);
        editor.setMxeSource("C:/Users/HeGuohong/Desktop/37.mxe");
        editor.createFrame(new ViewerMenuBar(editor)).setVisible(true);
    }
}